<template><div><h2 id="控制器" tabindex="-1"><a class="header-anchor" href="#控制器"><span>控制器</span></a></h2>
<p>在创建资源类并指定应该如何格式化资源数据之后，接下来要做的是创建控制器动作，并通过 RESTful API 将资源公开给最终用户。</p>
<p>Yii 提供了两个基本控制器类来简化创建 RESTful 动作的工作，即 <code v-pre>yii\rest\Controller</code> 和 <code v-pre>yii\rest\ActiveController</code>。这两个控制器之间的区别是，后者提供了一个默认的动作集（actions），专门设计来处理表示为活动记录（Active Record）的资源。因此，如果你正在使用活动记录，并对提供的内置动作感到舒适，你可以考虑从 <code v-pre>yii\rest\ActiveController</code> 扩展你的控制器类，它将允许你用最少的代码创建强大的 RESTful API。</p>
<p><code v-pre>yii\rest\Controller</code> 和 <code v-pre>yii\rest\ActiveController</code> 都提供了以下特性，其中一些特性将在接下来的几节中详细描述：</p>
<ul>
<li>HTTP method 验证；</li>
<li>内容协商和数据格式化；</li>
<li>认证；</li>
<li>速率限制。</li>
</ul>
<p><code v-pre>yii\rest\ActiveController</code> 另外还提供了以下功能：</p>
<ul>
<li>一组常用的操作：<code v-pre>index</code>, <code v-pre>view</code>, <code v-pre>create</code>, <code v-pre>update</code>, <code v-pre>delete</code>, <code v-pre>options</code>；</li>
<li>关于所请求的动作和资源的用户授权。</li>
</ul>
<h2 id="创建控制器类" tabindex="-1"><a class="header-anchor" href="#创建控制器类"><span>创建控制器类</span></a></h2>
<p>When creating a new controller class, a convention in naming the controller class is to use the type name of the resource and use singular form. For example, to serve user information, the controller may be named as <code v-pre>UserController</code>.</p>
<p>Creating a new action is similar to creating an action for a Web application. The only difference is that instead of rendering the result using a view by calling the <code v-pre>render()</code> method, for RESTful actions you directly return the data. The [[yii\rest\Controller::serializer|serializer]] and the [[yii\web\Response|response object]] will handle the conversion from the original data to the requested format. For example,</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionView</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token class-name static-context">User</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="过滤器" tabindex="-1"><a class="header-anchor" href="#过滤器"><span>过滤器</span></a></h2>
<p>Most RESTful API features provided by [[yii\rest\Controller]] are implemented in terms of <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide/structure-filters.md" target="_blank" rel="noopener noreferrer">filters</a>. In particular, the following filters will be executed in the order they are listed:</p>
<ul>
<li>[[yii\filters\ContentNegotiator|contentNegotiator]]: supports content negotiation, to be explained in the <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide/rest-response-formatting.md" target="_blank" rel="noopener noreferrer">Response Formatting</a> section;</li>
<li>[[yii\filters\VerbFilter|verbFilter]]: supports HTTP method validation;</li>
<li>[[yii\filters\auth\AuthMethod|authenticator]]: supports user authentication, to be explained in the <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide/rest-authentication.md" target="_blank" rel="noopener noreferrer">Authentication</a> section;</li>
<li>[[yii\filters\RateLimiter|rateLimiter]]: supports rate limiting, to be explained in the <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide/rest-rate-limiting.md" target="_blank" rel="noopener noreferrer">Rate Limiting</a> section.</li>
</ul>
<p>These named filters are declared in the [[yii\rest\Controller::behaviors()|behaviors()]] method. You may override this method to configure individual filters, disable some of them, or add your own filters. For example, if you only want to use HTTP basic authentication, you may write the following code:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>filters<span class="token punctuation">\</span>auth<span class="token punctuation">\</span>HttpBasicAuth</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">behaviors</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$behaviors</span> <span class="token operator">=</span> <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">behaviors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token variable">$behaviors</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'authenticator'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token class-name static-context">HttpBasicAuth</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$behaviors</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="cors" tabindex="-1"><a class="header-anchor" href="#cors"><span>CORS</span></a></h3>
<p>Adding the <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide/structure-filters.md#cors" target="_blank" rel="noopener noreferrer">Cross-Origin Resource Sharing</a> filter to a controller is a bit more complicated than adding other filters described above, because the CORS filter has to be applied before authentication methods and thus needs a slightly different approach compared to other filters. Also authentication has to be disabled for the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests" target="_blank" rel="noopener noreferrer">CORS Preflight requests</a> so that a browser can safely determine whether a request can be made beforehand without the need for sending authentication credentials. The following shows the code that is needed to add the [[yii\filters\Cors]] filter to an existing controller that extends from [[yii\rest\ActiveController]]:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>filters<span class="token punctuation">\</span>auth<span class="token punctuation">\</span>HttpBasicAuth</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">behaviors</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$behaviors</span> <span class="token operator">=</span> <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">behaviors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// remove authentication filter</span></span>
<span class="line">    <span class="token variable">$auth</span> <span class="token operator">=</span> <span class="token variable">$behaviors</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'authenticator'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token keyword">unset</span><span class="token punctuation">(</span><span class="token variable">$behaviors</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'authenticator'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// add CORS filter</span></span>
<span class="line">    <span class="token variable">$behaviors</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'corsFilter'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>filters<span class="token punctuation">\</span>Cors</span><span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// re-add authentication filter</span></span>
<span class="line">    <span class="token variable">$behaviors</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'authenticator'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token variable">$auth</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token comment">// avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)</span></span>
<span class="line">    <span class="token variable">$behaviors</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'authenticator'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'except'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'options'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$behaviors</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="扩展-activecontroller" tabindex="-1"><a class="header-anchor" href="#扩展-activecontroller"><span>扩展 ActiveController</span></a></h2>
<p>If your controller class extends from [[yii\rest\ActiveController]], you should set its [[yii\rest\ActiveController::modelClass|modelClass]] property to be the name of the resource class that you plan to serve through this controller. The class must extend from [[yii\db\ActiveRecord]].</p>
<h3 id="自定义-action" tabindex="-1"><a class="header-anchor" href="#自定义-action"><span>自定义 Action</span></a></h3>
<p>By default, [[yii\rest\ActiveController]] provides the following actions:</p>
<ul>
<li>[[yii\rest\IndexAction|index]]: list resources page by page;</li>
<li>[[yii\rest\ViewAction|view]]: return the details of a specified resource;</li>
<li>[[yii\rest\CreateAction|create]]: create a new resource;</li>
<li>[[yii\rest\UpdateAction|update]]: update an existing resource;</li>
<li>[[yii\rest\DeleteAction|delete]]: delete the specified resource;</li>
<li>[[yii\rest\OptionsAction|options]]: return the supported HTTP methods.</li>
</ul>
<p>All these actions are declared through the [[yii\rest\ActiveController::actions()|actions()]] method. You may configure these actions or disable some of them by overriding the <code v-pre>actions()</code> method, like shown the following,</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actions</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$actions</span> <span class="token operator">=</span> <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">actions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// disable the "delete" and "create" actions</span></span>
<span class="line">    <span class="token keyword">unset</span><span class="token punctuation">(</span><span class="token variable">$actions</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'delete'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token variable">$actions</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'create'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token comment">// customize the data provider preparation with the "prepareDataProvider()" method</span></span>
<span class="line">    <span class="token variable">$actions</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'index'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'prepareDataProvider'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token variable">$this</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'prepareDataProvider'</span><span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$actions</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">prepareDataProvider</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// prepare and return a data provider for the "index" action</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Please refer to the class references for individual action classes to learn what configuration options are available.</p>
<h3 id="执行访问检查" tabindex="-1"><a class="header-anchor" href="#执行访问检查"><span>执行访问检查</span></a></h3>
<p>When exposing resources through RESTful APIs, you often need to check if the current user has the permission to access and manipulate the requested resource(s). With [[yii\rest\ActiveController]], this can be done by overriding the [[yii\rest\ActiveController::checkAccess()|checkAccess()]] method like the following,</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token doc-comment comment">/**</span>
<span class="line"> * Checks the privilege of the current user.</span>
<span class="line"> *</span>
<span class="line"> * This method should be overridden to check whether the current user has the privilege</span>
<span class="line"> * to run the specified action against the specified data model.</span>
<span class="line"> * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.</span>
<span class="line"> *</span>
<span class="line"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token keyword">string</span></span> <span class="token parameter">$action</span> the ID of the action to be executed</span>
<span class="line"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>Model</span> <span class="token parameter">$model</span> the model to be accessed. If `null`, it means no specific model is being accessed.</span>
<span class="line"> * <span class="token keyword">@param</span> <span class="token class-name"><span class="token keyword">array</span></span> <span class="token parameter">$params</span> additional parameters</span>
<span class="line"> * <span class="token keyword">@throws</span> <span class="token class-name">ForbiddenHttpException</span> if the user does not have access</span>
<span class="line"> */</span></span>
<span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">checkAccess</span><span class="token punctuation">(</span><span class="token variable">$action</span><span class="token punctuation">,</span> <span class="token variable">$model</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">,</span> <span class="token variable">$params</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// check if the user can access $action and $model</span></span>
<span class="line">    <span class="token comment">// throw ForbiddenHttpException if access should be denied</span></span>
<span class="line">    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$action</span> <span class="token operator">===</span> <span class="token string single-quoted-string">'update'</span> <span class="token operator">||</span> <span class="token variable">$action</span> <span class="token operator">===</span> <span class="token string single-quoted-string">'delete'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$model</span><span class="token operator">-></span><span class="token property">author_id</span> <span class="token operator">!==</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">user</span><span class="token operator">-></span><span class="token property">id</span><span class="token punctuation">)</span></span>
<span class="line">            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>ForbiddenHttpException</span><span class="token punctuation">(</span><span class="token function">sprintf</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'You can only %s articles that you\'ve created.'</span><span class="token punctuation">,</span> <span class="token variable">$action</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The <code v-pre>checkAccess()</code> method will be called by the default actions of [[yii\rest\ActiveController]]. If you create new actions and also want to perform access check, you should call this method explicitly in the new actions.</p>
<blockquote>
<p>Tip: You may implement <code v-pre>checkAccess()</code> by using the <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide/security-authorization.md" target="_blank" rel="noopener noreferrer">Role-Based Access Control (RBAC) component</a>.</p>
</blockquote>
<blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


